#ifndef _MATERIAL_H_
#define _MATERIAL_H_

#include <memory>
#include <SERender/Resourses/Shader.h>
#include <SERender/Resourses/Texture.h>
#include <unordered_map>
#include <string>
#include <optional>
#include <any>
#include <SERender/Drive/Drive.h>
#include <SECore/Serializable.h>
#include <SECore/Descriptor.h>
#include <SECore/ResourcePayload.h>

namespace SEngine
{
    struct MaterialPath {
        char path[256]{0};
    };

    class Material
    {
    public:
        ENABLE_SERIALIZE(Material)
    public:
        Material() {}
        Material(const Material &);
        Material(ResRef<Shader> shader) : m_shader(shader) {}
        virtual ~Material() = default;

        template<typename T>
        void Set(const std::string & name, T t);

        template<typename T>
        std::optional<T> Get(const std::string & name);

        void Remove(const std::string & name);

        void Bind();
        void Unbind();

        ResRef<Shader> GetShader() { return m_shader; }
        void SetShader(ResRef<Shader> shader) { m_shader = shader; }

        PipeLineState & GetPSO() { return m_pso; }

    protected:
        PipeLineState m_pso;
        ResRef<Shader> m_shader;
        std::unordered_map<std::string, Shader::UniformType> m_uniformDatas;
    };
    
    template <typename T>
    inline void Material::Set(const std::string &name, T t)
    {
        if (m_uniformDatas.find(name) != m_uniformDatas.end()) {
            m_uniformDatas.at(name) = t;
        } else {
            m_uniformDatas.emplace(name, t);
        }
    }
    
    template <typename T>
    inline std::optional<T> Material::Get(const std::string &name)
    {
        if (auto data = m_uniformDatas.find(name); data != m_uniformDatas.end()) {
            return std::get<T>(data->second);
        }
        return std::nullopt;
    }
} // namespace SEngine

#endif